package geradordao;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Iana
 */
public class AnalisadorSemantico {

    private List<String> erros = new ArrayList<String>();
    private No raiz;
    private Token escopo;

    public AnalisadorSemantico(No raiz) {
        this.raiz = raiz;
    }

    public boolean analisar() {
        analisar(raiz);
        if (erros.size() == 0) {
            return true;
        }
        return false;

    }

    public Object analisar(No no) {
        //um para cada não terminal
        if (no.getTipo() == TipoNo.No_QueryList) {
            return QueryList(no);
        } else if (no.getTipo() == TipoNo.No_Query) {
            return Query(no);
        } else if (no.getTipo() == TipoNo.No_CreateStm) {
            return CreateStm(no);
        } else if (no.getTipo() == TipoNo.No_FieldDefList) {
            return FieldDefList(no);
        } else if (no.getTipo() == TipoNo.No_FieldDefList2) {
            return FieldDefList2(no);
        } else if (no.getTipo() == TipoNo.No_FieldDef) {
            return FieldDef(no);
        } else if (no.getTipo() == TipoNo.No_Type) {
            return Type(no);
        } else if (no.getTipo() == TipoNo.No_ConstraintList) {
            return ConstraintList(no);
        } else if (no.getTipo() == TipoNo.No_Constraint) {
            return Constraint(no);
        } else if (no.getTipo() == TipoNo.No_ConstraintType) {
            return ConstraintType(no);
        } else if (no.getTipo() == TipoNo.No_IdList) {
            return IdList(no);
        } else if (no.getTipo() == TipoNo.No_IdList2) {
            return IdList2(no);
        }
        return null;
    }

    //<Query_List> ::= <Query> ';' <Query_List> |
    private Object QueryList(No no) {
        if (no.getFilhos().size() > 0) {
            analisar(no.getFilho(0));
            analisar(no.getFilho(1));
        }
        return null;
    }

    // <Query> ::= <Alter Stm> | <Create Stm>
    private Object Query(No no) {
        analisar(no.getFilho(0));
        return null;
    }

    //<CreateStm>  ::= create table Id '(' <Field Def List> ')' ';'
    private Object CreateStm(No no) {
        Token tokenId = no.getFilho(2).getToken();
        String tipoIdTable = TabelaSimbolos.getTipoSimbolo(tokenId);
        if (tipoIdTable != null) {
            erros.add("Erro semântico: identificador de tabela redeclarado: " + tokenId);
        } else {
            TabelaSimbolos.setTipoSimbolo(tokenId, "table");
        }
        analisar(no.getFilho(4));

        return null;
    }

    //<FieldDefList> ::= <FieldDef> <FieldDefList2>
    private Object FieldDefList(No no) {
        analisar(no.getFilho(0));
        analisar(no.getFilho(1));
        return null;
    }

    //<FieldDefList2> ::= ',' <Field Def> <Field Def List 2> | <ConstraintList>
    private Object FieldDefList2(No no) {
        if (no.getFilhos().size() == 3) {
            analisar(no.getFilho(1));
            analisar(no.getFilho(2));
        } else if (no.getFilhos().size() == 1) {
            analisar(no.getFilho(0));
        }
        return null;
    }

    //<FieldDef>   ::= Id <Type> <Not Null>
    private Object FieldDef(No no) {
        Token tokenId = no.getFilho(0).getToken();
        Token type = (Token) analisar(no.getFilho(1));
        String tipoId = TabelaSimbolos.getTipoSimbolo(tokenId);
        String tipoCompativel = TabelaSimbolos.getTipoJavaEquivalente(type.getImagem());
        if ((tipoId != null) && (!tipoCompativel.equals(tipoId))) {
            erros.add("Erro semântico: identificador de campo redeclarado: " + tokenId);
        } else {
            TabelaSimbolos.setTipoSimbolo(tokenId, tipoCompativel);
        }
        return null;
    }

    //<Type>  ::=  bit  | date  |  time | timestamp |  decimal  |  real |  float
    //|  smallint |  integer | int  |  interval |  character |  varchar <Tamanho>
    private Object Type(No no) {
        return no.getFilho(0).getToken();

    }

    //<ConstraintList> ::= ',' <ConstraintType> <ConstraintList> |
    private Object ConstraintList(No no) {
        if (no.getFilhos().size() > 0) {
            analisar(no.getFilho(1));
            analisar(no.getFilho(2));
        }
        return null;
    }

    //<Constraint> ::= constraint Id <Constraint Type>  | <Constraint Type>
    private Object Constraint(No no) {
        if (no.getFilhos().size() > 1) {
            Token tokenId = no.getFilho(1).getToken();
            Token type = (Token) analisar(no.getFilho(2));
            String tipoId = TabelaSimbolos.getTipoSimbolo(tokenId);
            String tipoCompativel = TabelaSimbolos.getTipoJavaEquivalente(type.getImagem());
            if ((tipoId != null) && (!tipoCompativel.equals(tipoId))) {
                erros.add("Erro semântico: identificador de campo redeclarado: " + tokenId);
            } else {
                TabelaSimbolos.setTipoSimbolo(tokenId, tipoCompativel);
            }
        } else {
            analisar(no.getFilho(0));
        }
        return null;
    }

    // <Constraint Type> ::= primary key '(' <Id List> ')'| foreign key '(' <Id List> ')' references Id '(' <Id List> ')'
    public Object ConstraintType(No no) {
        //analisar(no.getFilho(3));
        List<Token> keyIds = (List<Token>) analisar(no.getFilho(3));
        return null;
    }

    //<Id List> ::= Id <Id List 2>
    public Object IdList(No no) {
        List<Token> idList2 = (List<Token>) analisar(no.getFilho(1));
        idList2.add(no.getFilho(0).getToken());
        return idList2;
    }

    //<Id List 2> ::= ',' Id <Id List 2> |
    public Object IdList2(No no) {
        if (no.getFilhos().isEmpty()) {
            return new ArrayList<Token>();
        } else {
            List<Token> idList2 = (List<Token>) analisar(no.getFilho(2));
            idList2.add(no.getFilho(1).getToken());
            return idList2;
        }
    }

    void setaErro(String metodo, String esperado, String veio, Integer linha) {
        erros.add("Falha: No metodo " + metodo + " espera-se " + esperado + " na linha " + linha + " veio " + veio);
    }

    public void printErros() {
        for (String erro : erros) {
            System.out.println(erro);
        }
    }

    public boolean temErros() {
        return erros.size() > 0;
    }
}
